
#include <ctype.h>
#include <kernel.h>

#define _8259A_MASTER_PORT1         0x20
#define _8259A_MASTER_PORT2         0x21

#define _8259A_SLAVE_PORT1          0xA0
#define _8259A_SLAVE_PORT2          0xA1

#define _8259A_ICW1_PREFIX          (1 << 4)

#define _8259A_ICW1_LEVELTRIGGER    (1 << 3)
#define _8259A_ICW1_8085_4B_VECTOR  (1 << 2)
#define _8259A_ICW1_SINGLE_CHIP     (1 << 1)
#define _8259A_ICW1_NEED_ICW4       (1 << 0)

#define _8259A_ICW2_MASK            0x07

#define _8259A_ICW3_MASTER_VALUE    0x04
#define _8259A_ICW3_SLAVE_VALUE     0x02

#define _8259A_ICW4_AUTO_EOI        (1 << 1)
#define _8259A_ICW4_80x86_MODE      (1 << 0)

#define _8259A_OCW1_ENABLE_ALL      0

#define _8259A_OCW2_EOI             (1 << 5)

static int init_flag = FALSE;

void init_8259a(int start_vector)
{
    unsigned char d;

    if ((start_vector & _8259A_ICW2_MASK) != 0 || start_vector > 255)
        return;

    /* ICW1 with RESET flag and ICW4 needed flag */

    d = _8259A_ICW1_PREFIX | _8259A_ICW1_NEED_ICW4;

    out_b(_8259A_MASTER_PORT1, d); /* write ICW1 to Master chip PORT 1 */

    asm_delay();

    out_b(_8259A_SLAVE_PORT1, d); /* write ICW1 to Slave chip PORT 1 */

    asm_delay();

    /* ICW2 ~ ICW4 should be written to PORT 2 */

    d = start_vector;

    out_b(_8259A_MASTER_PORT2, d); /* write start vector to Master chip PORT 2 */

    asm_delay();

    d = start_vector + 8;

    out_b(_8259A_SLAVE_PORT2, d); /* write start vector + 8 to Master chip PORT 2 */

    asm_delay();

    /* ICW3: Master IRQ2 is connected with Slave chip */

    d = _8259A_ICW3_MASTER_VALUE;

    out_b(_8259A_MASTER_PORT2, d);

    asm_delay();

    d = _8259A_ICW3_SLAVE_VALUE;

    out_b(_8259A_SLAVE_PORT2, d);

    asm_delay();

    /* ICW4: set to 80x86 mode */

    d = _8259A_ICW4_80x86_MODE;

    out_b(_8259A_MASTER_PORT2, d);

    asm_delay();

    out_b(_8259A_SLAVE_PORT2, d);

    asm_delay();

    /* OCW1: should be written to PORT2, enable all interrupts */

    d = _8259A_OCW1_ENABLE_ALL;

    out_b(_8259A_MASTER_PORT2, d);

    asm_delay();

    out_b(_8259A_SLAVE_PORT2, d);

    asm_delay();

    init_flag = TRUE;

    return;
}

void eoi_8259a()
{
    unsigned char d;

    if (init_flag == FALSE)
        return;

    /* EOI use PORT1 */

    d = _8259A_OCW2_EOI;

    out_b(_8259A_MASTER_PORT1, d);

    asm_delay();

    out_b(_8259A_SLAVE_PORT1, d);

    asm_delay();

    return;
}
